#pragma once 
#include <iostream>
#include <memory>
#include <unistd.h>
#include <string>
#include "Mutex.hpp"
#include <filesystem>
#include <fstream>
#include <time.h>
#include <sstream>

namespace LogModule 
{
    using namespace MpthreayMutex;
    const std::string defaultlogpath = "./log/";    //log日志所保存的路径
    const std::string defaultlogname = "log.txt";  //log文件名

    //日志等级
    enum class LogLevel
    {
        DEBUG = 1,  //调式
        INFO,       //正常
        WARNING,    //警告
        ERROR,      //错误
        FATAL       //致命的
    };

    //获取时间信息
    std::string GerCurrTime()
    {
        time_t timp = time(nullptr);
        struct tm t;
        localtime_r(&timp,&t);
        char buffer[1024];
        snprintf(buffer,sizeof(buffer),"%4d-%02d-%02d %02d:%02d:%02d", 
            t.tm_year+1900,
            t.tm_mon+1,
            t.tm_wday+1,
            t.tm_hour,
            t.tm_min,
            t.tm_sec);
        return buffer;
    }
    std::string loglevelToString(LogLevel loglevel)
    {
        switch(loglevel)
        {
            case LogLevel::DEBUG:
                return "DEBUG";
            case LogLevel::INFO:
                return "INFO";
            case LogLevel::WARNING:
                return "WARNING";
            case LogLevel::ERROR:
                return "ERROR";
            case LogLevel::FATAL:
                return "FATAL";
            default:
                return "NONE";                                                                            
        }
    }

    //1.策略模式
    class Stragety
    {
    public:
        virtual ~Stragety() = default; //多态的话先把析构处理了
        virtual void Synclog(const std::string& message) = 0;   //基类提供抽象类，有子类提供实现。1. 不能实列化对象 2. 但是可以创建指针
    };

    //默认策略模式 - 向显示器输出日志信息
    class ConsoleStragety : public Stragety
    {
    public:
        //override： 如果没有不是虚函数重写就报错
        void Synclog(const std::string& message) override 
        {
            LockGround lock(_mtx); // 多线程的话日志输出也是临界资源
            std::cout << message << std::endl; 
        }
        ~ConsoleStragety()
        {
            
        }
    private:
        Mutex _mtx; 
    };

    class FileLogStragety : public Stragety
    {
    public:
        FileLogStragety(const std::string& logpath = defaultlogpath,const std::string logname = defaultlogname)
            :_logpath(logpath),
            _logname(logname)
        {
            LockGround lock(_mtx);
            //检查对应的目录以及文件是否存在
            if (std::filesystem::exists(_logpath)) //如果存在的话直接返回就可以
            {
                return;
            }
            else 
            {
                //创建文件夹
                try 
                {
                    std::filesystem::create_directories(_logpath);
                }
                catch(const std::filesystem::filesystem_error & e)
                {
                    std::cout << "创建文件夹失败" << std::endl;
                    std::cout << e.what() << std::endl;
                }
            }
        }
        void Synclog(const std::string& message) override
        {
            LockGround lock(_mtx);
            std::string filename = _logpath + _logname;
            std::ofstream  out(filename.c_str(),std::ios::app); //如果不存在创建，存在追加
            if (!out.is_open()) return;
            out << message << std::endl;
            out.close();
        }
        ~FileLogStragety()
        {}
    private:
        std::string _logpath;
        std::string _logname;
        Mutex _mtx; 
    };  


    //具体的日志类
    class Logger
    {
    public:
        Logger()
        {
            _strategy = std::make_shared<ConsoleStragety>();
        }
        void EnableConsoleStragety()
        {
            _strategy = std::make_shared<ConsoleStragety>();
        }
        void EnableFilesystemStragety()
        {
             _strategy = std::make_shared<FileLogStragety>();
        }

        //为什么要实现这一个内部类呢?
        class LogMessage
        {
        public:
            LogMessage(LogLevel level,int line,const std::string& filename,Logger& logger)
                :_level(level),
                _line(line),
                _filename(filename),
                _logger(logger)
            {
                //初始化_loginfo 
                //1. 时间：
                std::stringstream ss;
                ss  << "[" << GerCurrTime() << "] "
                    << "[" << loglevelToString(_level) << "] "
                    << "[" << getpid() << "] "
                    << "[" << filename << "] "
                    << "[" << _line << "] " 
                    << " - ";
                _loginfo += ss.str();
            }
            //因为不确定传入什么类型 ,调用方式： LOG(DUBUG) << "hello world" << 3 << 2.2 << 1;
            template<class T>
            LogMessage& operator<<(const T& info)
            {
                std::stringstream ss;
                ss << info;
                _loginfo += ss.str();
                return *this; //返回调用方便下一调用
                //注意这里需要引用返回，不然的话会创建两次
            }
            ~LogMessage()
            {
                //更新日志信息本质是输出?
                if (_logger._strategy)  //调用析构之后以特定方式输出日志
                {
                    _logger._strategy->Synclog(_loginfo);
                }
            }
        private:    
            std::string _loginfo; //总的信息
            LogLevel _level;
            int _line;
            std::string _filename;
            Logger& _logger;
        };

        LogMessage operator()(LogLevel level,int line,const std::string& filename)
        {
            //这里需要注意理论上应该创建两个LogMessage的，但是编译器直接给优化掉了，避免了不必要的拷贝
            return LogMessage(level,line,filename,*this);
        }

        ~Logger()
        {

        }
    private:
        std::shared_ptr<Stragety> _strategy;
    };

    Logger logger;
    //定义调用的宏。，返回的是匿名对象，生命周期在当前行，然后调用LogMessage的析构
    #define LOG(type) logger(type,__LINE__,__FILE__) 
    //定义转换策略模式的宏
    #define ENABLE_CONSOLE_LOG_STRATEGY() logger.EnableConsoleStragety()
    #define ENABLE_FILESYSTEM_LOG_STRATEGY() logger.EnableFilesystemStragety()
}
